#include "QPropertyFormWidget.h"

#include <QFormLayout>
#include "private/QPropertyFormWidget_p.h"

#include <QWidgetProperty.h>
#include <QEvent>
#include <QKeyEvent>

using namespace QPE;

QPropertyFormWidget::QPropertyFormWidget(const QString &title, const QString &description, const QList<QProperty*>& properties, QWidget *parent)
	: QGroupBox(title, parent), d_ptr(new QPropertyFormWidgetPrivate(properties))
{
	build();
	setToolTip(description);
	setWhatsThis(description);
}

QPropertyFormWidget::QPropertyFormWidget(QProperty *parent_property, QWidget *parent)
	: QGroupBox(parent), d_ptr(new QPropertyFormWidgetPrivate())
{
	if(parent_property) {
		d_ptr->Properties = parent_property->getChildren();
		build();
		setTitle(parent_property->getName());
		setToolTip(parent_property->getDescription());
		setWhatsThis(parent_property->getDescription());
	}
}

QPropertyFormWidget::QPropertyFormWidget(QPropertyFormWidgetPrivate *d_pointer, QWidget *parent, const QString &title, const QString &description)
	: QGroupBox(title, parent), d_ptr(d_pointer)
{
	build();
	setToolTip(description);
	setWhatsThis(description);
}

QPropertyFormWidget::~QPropertyFormWidget()
{
	delete d_ptr;
}


void QPropertyFormWidget::build()
{
	// Clear the old content, delete old widgets
	d_ptr->EditorWidgets.clear();
	if(layout()) {
		QLayoutItem *child;
		while (layout()->count() > 0 && (child = layout()->takeAt(0)) != 0) {
			if(child->widget())
				delete child->widget();
			delete child;
		}
		delete layout();
	}

	// Create new content
	if(d_ptr->Properties.isEmpty()) return;	//... only if there are properties

	QFormLayout* tmpFormLayout = new QFormLayout(this);
	setLayout(tmpFormLayout);

	for(int i = 0; i < d_ptr->Properties.count(); ++i) {
		// Get the current property
		QProperty* tmpProperty = d_ptr->Properties.value(i, NULL);
		if(!tmpProperty) continue;

		if(tmpProperty->getRowCount() > 0) {
			// Child properties, the property itself is not being added
			QPropertyFormWidget* tmpNewFormWidget = new QPropertyFormWidget(tmpProperty, this);
			tmpFormLayout->addRow(tmpNewFormWidget);
			d_ptr->EditorWidgets.append(QPropertyFormWidgetPrivate::SEditorWidget(tmpNewFormWidget));
			tmpNewFormWidget->setCommitBehaviour(d_ptr->UpdateEditors);
		} else if(tmpProperty->type() == "widget") {
			QWidgetProperty* tmpWidgetProperty = static_cast<QWidgetProperty*>(tmpProperty);
			tmpFormLayout->addRow(tmpWidgetProperty->getWidget());
			d_ptr->EditorWidgets.append(QPropertyFormWidgetPrivate::SEditorWidget(tmpWidgetProperty->getWidget()));
		} else {
			// Add property (no child properties)
			// Create the editor (if it doesn't work, create empty widget)
			QWidget* tmpEditor = tmpProperty->createEditor(this, QStyleOptionViewItem(), NULL);
			if(!tmpEditor)
				tmpEditor = new QWidget(this);

			// set tooltip and whats this
			tmpEditor->setToolTip(tmpProperty->getDescription());
			tmpEditor->setWhatsThis(tmpProperty->getDescription());

			// Install event filter
			tmpEditor->installEventFilter(this);

			// Set the editor data
			tmpProperty->setEditorData(tmpEditor);

			// add new row
			tmpFormLayout->addRow(tmpProperty->getName(), tmpEditor);
			d_ptr->EditorWidgets.append(QPropertyFormWidgetPrivate::SEditorWidget(tmpEditor));
		}
	}
}

void QPropertyFormWidget::commitData()
{
	for(int i = 0; i < d_ptr->Properties.count(); ++i)
		commitData(i);
}

void QPropertyFormWidget::loadData()
{
	for(int i = 0; i < d_ptr->Properties.count(); ++i)
		loadData(i);
}

void QPropertyFormWidget::commitData(int row)
{
	if(row < 0 || row >= d_ptr->EditorWidgets.count() || row >= d_ptr->Properties.count()) return;

	QPropertyFormWidgetPrivate::SEditorWidget& tmpEditorWidget = d_ptr->EditorWidgets[row];
	QProperty* tmpProperty = d_ptr->Properties[row];
	if(tmpEditorWidget.FormWidget)
		tmpEditorWidget.FormWidget->commitData();
	else if(tmpEditorWidget.Editor && tmpProperty) {
		tmpProperty->setValue(tmpProperty->getEditorData(tmpEditorWidget.Editor));
		emit propertyDataSubmitted(tmpProperty);
	}
}

void QPropertyFormWidget::loadData(int row)
{
	if(row < 0 || row >= d_ptr->EditorWidgets.count() || row >= d_ptr->Properties.count()) return;

	QPropertyFormWidgetPrivate::SEditorWidget& tmpEditorWidget = d_ptr->EditorWidgets[row];
	QProperty* tmpProperty = d_ptr->Properties[row];
	if(tmpEditorWidget.FormWidget)
		tmpEditorWidget.FormWidget->loadData();
	else if(tmpEditorWidget.Editor && tmpProperty) {
		tmpProperty->setEditorData(tmpEditorWidget.Editor);
	}
}

void QPropertyFormWidget::setCommitBehaviour(bool auto_commit)
{
	d_ptr->UpdateEditors = auto_commit;

	QList<QPropertyFormWidget*> tmpChildFormWidgets = getChildPropertyFormWidgets();
	foreach(QPropertyFormWidget* tmpChild, tmpChildFormWidgets) {
		if(tmpChild)
			tmpChild->setCommitBehaviour(auto_commit);
	}
}

QList<QPropertyFormWidget *> QPropertyFormWidget::getChildPropertyFormWidgets() const
{
	QList<QPropertyFormWidget *> tmpResult;
	foreach(const QPropertyFormWidgetPrivate::SEditorWidget& tmpEditorWidget, d_ptr->EditorWidgets) {
		if(tmpEditorWidget.FormWidget)
			tmpResult.append(tmpEditorWidget.FormWidget);
	}

	return tmpResult;
}

bool QPropertyFormWidget::eventFilter(QObject *object, QEvent *event)
{
	if(!d_ptr->UpdateEditors)
		return false;

	QWidget* editor = qobject_cast<QWidget*>(object);
	if (!editor)
		return false;

	if (event->type() == QEvent::KeyPress) {
		switch (static_cast<QKeyEvent *>(event)->key()) {
			case Qt::Key_Tab:
			case Qt::Key_Backtab:
			case Qt::Key_Enter:
			case Qt::Key_Return:
			case Qt::Key_Escape:
				commitData(editor);
				event->accept();
				return true;
			default:
				return false;
		}
	} else if (event->type() == QEvent::FocusOut || (event->type() == QEvent::Hide && editor->isWindow())) {
		commitData(editor);
		return false;
	} else if (event->type() == QEvent::ShortcutOverride) {
		if (static_cast<QKeyEvent*>(event)->key() == Qt::Key_Escape) {
			commitData(editor);
			event->accept();
			return true;
		}
	}

	// Default:
	return false;
}

void QPropertyFormWidget::commitData(QWidget *editor)
{
	if(!editor)
		return;

	for(int i = 0; i < d_ptr->EditorWidgets.count(); ++i) {
		QPropertyFormWidgetPrivate::SEditorWidget& tmpEditorWidget = d_ptr->EditorWidgets[i];
		if(tmpEditorWidget.Editor == editor)
			commitData(i);
	}
}
